﻿// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdio.h>
#include <direct.h>
#include <io.h>
#include "..\common\D2LoaderVersion.h"

#define PATCH_FINISH    0xFFFFFFFF
#define PATCH_JMP               0x000000E9
#define PATCH_CALL              0x000000E8
#define PATCH_RETN              0x000000C3
#define PATCH_RETN4             0x000004C2
#define PATCH_RETN8             0x000008C2
#define PATCH_RETN0C            0x00000CC2
#define PATCH_RETN10            0x000010C2
#define PATCH_RETN14            0x000014C2
#define PATCH_RETN18            0x000018C2
#define PATCH_RETN1C            0x00001CC2
#define PATCH_NOPBLOCK          0x90909090

typedef struct 
{
    DWORD dwAddress;
    DWORD dwData;
    BOOL boolRelative;
    size_t iPatchSize;
} DLLPatchStrc;

static BOOL D2Loader_ApplyPatch(void* hGame, const DWORD dwBaseAddress, const DLLPatchStrc* pstPatch)
{
    while ( PATCH_FINISH != pstPatch->dwAddress )
    {
        int iReturn = 0;
        DWORD dwAddress = pstPatch->dwAddress;
        if  ( !dwAddress )
        {
            return FALSE;
        }

        dwAddress += dwBaseAddress;

        DWORD dwData = pstPatch->dwData;
        if ( pstPatch->boolRelative )
        {
            dwData = dwData - (dwAddress + sizeof(dwData));
        }

        void* hAddress = (void*)dwAddress;
        DWORD dwOldPage;

        if ( pstPatch->iPatchSize > 0 )
        {
            BYTE abBuffer[1024];

            for ( size_t i = 0; i < pstPatch->iPatchSize; i++ )
            {
                abBuffer[i] = (BYTE)dwData;
            }

            VirtualProtect(hAddress, pstPatch->iPatchSize, PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &abBuffer, pstPatch->iPatchSize, 0);
            VirtualProtect(hAddress, pstPatch->iPatchSize, dwOldPage, 0);
        }
        else
        {
            VirtualProtect(hAddress, sizeof(dwData), PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &dwData, sizeof(dwData), 0);
            VirtualProtect(hAddress, sizeof(dwData), dwOldPage, 0);
        }

        if ( 0 == iReturn )
        {
            return FALSE;
        }

        pstPatch++;
    }

    return TRUE;
}

static char m_acRunningPath[D2LOADER_MAXPATH] = {0};

static BOOL __stdcall D2Loader_SRegLoadString(char *keyname, char *valuename, int a3, char *buffer, size_t buffersize)
{
    if ( !_stricmp("Diablo II", keyname) && !_stricmp("InstallPath", valuename) )
    {
        strcpy_s(buffer, buffersize, m_acRunningPath);
        return TRUE;
    }

    if ( !_stricmp("Diablo II", keyname) && (!_stricmp("Save Path", valuename) || !_stricmp("NewSavePath", valuename)) )
    {
        sprintf_s(buffer, buffersize, "%s\\Save", m_acRunningPath);
        if ( _access(buffer, 0) == -1 )
        {
            _mkdir(buffer);
        }
        return TRUE;
    }

    return FALSE;
}

DWORD m_dwRegLoadRetAddr = 0;
static __declspec(naked) void D2Loader_SRegLoadString_asm()
{
    __asm
    {
        pushad
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        call D2Loader_SRegLoadString
        test eax,eax
        popad
        jz orig_code
        mov eax, 0x1
        retn 0x14
orig_code:
        push ebp
        mov ebp,esp
        mov ecx,dword ptr ss:[ebp+0x8]
        jmp m_dwRegLoadRetAddr
    }
}

static void D2Loader14d_PatchModPath()
{
    static const DLLPatchStrc astPatches[] =
    {
        {0x14E50, PATCH_JMP, FALSE, 0x01},
        {0x14E51, (DWORD)D2Loader_SRegLoadString_asm, TRUE, 0x00},
        {0x14E55, PATCH_NOPBLOCK, FALSE, 0x01},

        {PATCH_FINISH} // this must be the last entry in the array!
    };

    m_dwRegLoadRetAddr = (DWORD)GetModuleHandle(NULL) + 0x14E56;
    D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle(NULL), astPatches);
}

static void D2Loader14d_PatchMultiopen()
{
    static const DLLPatchStrc astPatches[] =
    {
        {0xF562B, 0xEB, FALSE, 0x01},

        {PATCH_FINISH}
    };

    D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle(NULL), astPatches);
}

static BOOL __stdcall D2Loader_RegSaveString(char *keyname, char *valuename, BYTE flags, char *string)
{
    if ( !_stricmp("Diablo II", keyname) &&
        (!_stricmp("InstallPath", valuename) || !_stricmp("Save Path", valuename) || !_stricmp("NewSavePath", valuename)) )
    {
        //不写注册表
        return TRUE;
    }

    return FALSE;
}

static DWORD m_dwRegSaveStringRet = 0;
static __declspec(naked) void D2Loader_RegSaveString_asm()
{
    __asm
    {
        pushad
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        call D2Loader_RegSaveString
        test eax,eax
        popad
        jz orig_code
        mov eax, 0x1
        retn 0x10
orig_code:
        push ebp
        mov ebp,esp
        push ebx
        push esi
        jmp m_dwRegSaveStringRet
    }
}

static void D2Loader14d_RemoveRegistryUpdate()
{
    //直接从storm.dll入手了
    DLLPatchStrc astPatches[] =
    {
        {0, 0, 0, 0},
        {0, 0, 0, 0},

        {PATCH_FINISH} // this must be the last entry in the array!
    };
    DWORD dwOffset = 0x15070;

    astPatches[0].dwAddress = dwOffset;
    astPatches[0].dwData = PATCH_JMP;
    astPatches[0].boolRelative = FALSE;
    astPatches[0].iPatchSize = 1;
    astPatches[1].dwAddress = dwOffset + 1;
    astPatches[1].dwData = (DWORD)D2Loader_RegSaveString_asm;
    astPatches[1].boolRelative = TRUE;
    astPatches[1].iPatchSize = 0;
    m_dwRegSaveStringRet = (DWORD)GetModuleHandle(NULL) + dwOffset + 5;
    D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle(NULL), astPatches);
}

static void D2Loader14d_InitData()
{
    char *pcTemp = NULL;

    GetModuleFileName(NULL, m_acRunningPath, sizeof(m_acRunningPath)); 
    pcTemp = strrchr(m_acRunningPath, '\\');
    if ( NULL != pcTemp )
    {
        *pcTemp = 0;
    }
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            D2Loader14d_InitData();
            D2Loader14d_PatchModPath();
            D2Loader14d_RemoveRegistryUpdate();
            D2Loader14d_PatchMultiopen();
            break;

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

